The skimage (scikit-image) library provides a range of morphological operations, including:
OpenCV, another popular computer vision library, offers various morphological operations such as:
In the exploration of Morphological Image Processing, I primarily worked with two essential tools:
# necessary Imports
import numpy as np
import matplotlib.pyplot as plt
from skimage.io import imread, imshow
from skimage.draw import disk
from skimage.morphology import (erosion, dilation, closing, opening,
area_closing, area_opening)
from skimage.color import rgb2gray
# Define a Structuring Element
element = np.array([[0,2,0],
[1,88,1],
[0,1,0]])
# Show a structuring Element
plt.imshow(element)
""" Create two adjacent circles """
# Create Canvas for Image
circle_image = np.zeros((25, 40))
# Cretae two disks with center (,) and radius 8 and set their pixel to white 1
circle_image[disk((12, 12), 8)] = 1
circle_image[disk((12, 28), 8)] = 1
# Set random pixels to 1, to simulate noise
for x in range(20):
circle_image[np.random.randint(25), np.random.randint(40)] = 1
# View Image
imshow(circle_image)
<matplotlib.image.AxesImage at 0x7f57138ec430>
# Apply Erosion and Dilation
img_erosion= erosion(circle_image, element)
img_dilation = dilation(circle_image, element)
fig, ax = plt.subplots(1,2, figsize=(15,5))
ax[0].imshow(img_erosion, cmap='gray')
ax[0].set_title('Eroded Image')
ax[1].imshow(img_dilation, cmap='gray')
ax[1].set_title('Dilated Image')
Text(0.5, 1.0, 'Dilated Image')
# Apply multiple dilations
def multi_dil(im, num, element=element):
for i in range(num):
im = dilation(im, element)
return im
# Apply Multiple Erosions
def multi_ero(im, num, element=element):
for i in range(num):
im = erosion(im, element)
return im
fig, ax = plt.subplots(1,2, figsize=(15,5))
ax[0].imshow(multi_ero(circle_image, 5, element), cmap='gray')
ax[0].set_title('Multi-Eroded Image')
ax[1].imshow(multi_dil(circle_image, 2, element), cmap='gray')
ax[1].set_title('Multi-Dilated Image')
Text(0.5, 1.0, 'Multi-Dilated Image')
# Apply Opening ( Erosion followed by Dilation )
fig, ax = plt.subplots(1,2, figsize=(15,5))
ax[0].imshow(opening(circle_image, element), cmap='gray')
ax[0].set_title('Opened Image')
ax[1].imshow(closing(circle_image, element), cmap='gray')
ax[1].set_title('Closed Image')
Text(0.5, 1.0, 'Closed Image')
# Apply on real Image
# 1 : Random Image
# Read Image file
paysage = imread('Paysage.jpg')
fig, ax = plt.subplots(1,2, figsize=(12,6))
ax[0].imshow(paysage);
ax[0].set_title('Original Image')
binary = rgb2gray(paysage)<0.15
ax[1].imshow(binary)
ax[1].set_title('Binarized Image')
Text(0.5, 1.0, 'Binarized Image')
# Define a Structuring Element
element = np.array([[0,0,0,0,0,0,0],
[0,0,1,1,1,0,0],
[0,1,1,1,1,1,0],
[0,1,1,1,1,1,0],
[0,1,1,1,1,1,0],
[0,0,1,1,1,0,0]])
# Apply multiple erosions of binary image
multi_eroded = multi_ero(binary, 2, element)
imshow(multi_eroded)
<matplotlib.image.AxesImage at 0x7f5711612df0>
# Apply multiple dilations of binary image
multi_dilated= multi_dil(binary, 5, element)
imshow(multi_dilated)
<matplotlib.image.AxesImage at 0x7f571178bf10>
# Apply Area opening
area_morphed = area_closing(area_closing(multi_dilated, 1000), 11000)
imshow(area_morphed)
<matplotlib.image.AxesImage at 0x7f57114a0b50>
# Different Image
# Image with Lattice frame
# Apply on real Image
# Read Image file
leaves = imread('leaves.jpg')
fig, ax = plt.subplots(1,2, figsize=(12,6))
ax[0].imshow(leaves)
ax[0].set_title('Original Image')
binary = rgb2gray(leaves)<0.15
ax[1].imshow(binary)
ax[1].set_title('Binarized Image')
Text(0.5, 1.0, 'Binarized Image')
# Define structuring element
element = np.array([[0,0,0,0,0,0,0],
[0,0,1,1,1,0,0],
[0,1,1,1,1,1,0],
[0,1,1,1,1,1,0],
[0,1,1,1,1,1,0],
[0,0,1,1,1,0,0],
[0,0,0,0,0,0,0]])
# Multiple erosion
multi_eroded = multi_ero(binary, 2, element)
imshow(multi_eroded)
<matplotlib.image.AxesImage at 0x7f5711281130>
# Opening
opened = opening(multi_eroded)
imshow(opened)
<matplotlib.image.AxesImage at 0x7f57111efcd0>
import cv2
import numpy as np
# Read Image
image = cv2.imread("leaves.jpg")
# Define Structuring Element
str_element= np.ones((25, 25), np.uint8)
# Erosion
# Using cv2.erode() method
image_erode = cv2.erode(image, str_element)
filename = 'image_eroded1.jpg'
# Using cv2.imwrite() method
# Save img
cv2.imwrite(filename, image_erode)
# View
imshow(image_erode)
<matplotlib.image.AxesImage at 0x7f571116de80>
# Dilation
# Using cv2.erode() method
image_dilation = cv2.dilate(image, str_element)
filename = 'image_dilation1.jpg'
# Using cv2.imwrite() method
# Saving the image
cv2.imwrite(filename, image_dilation)
# View
imshow(image_dilation)
<matplotlib.image.AxesImage at 0x7f57110f3190>
# Create a border for Image
# Using cv2.copyMakeBorder() method
image_border = cv2.copyMakeBorder(image, 5, 5, 5, 5, cv2.BORDER_CONSTANT, None, value = 2)
filename = 'image_border1.jpg'
# Save the image
cv2.imwrite(filename, image_border)
imshow(image_border)
<matplotlib.image.AxesImage at 0x7f57110e5a30>
# Make Mirrored Border
image_border_mirr = cv2.copyMakeBorder(image, 100, 100, 100, 100, cv2.BORDER_REFLECT)
filename = 'image_border_mirrored.jpg'
# Using cv2.imwrite() method
# Saving the image
cv2.imwrite(filename, image_border_mirr)
imshow(image_border_mirr)
<matplotlib.image.AxesImage at 0x7f570f4d3460>
"""Intensity Transformations """
# Log transformation
# Apply log transform.
c = 255/(np.log(1 + np.max(image)))
log_transformed = np.array(c * np.log(1 + image), dtype = np.uint8)
cv2.imwrite('log_transformed.jpg', log_transformed)
imshow(log_transformed)
<ipython-input-59-a467beec5aa6>:7: RuntimeWarning: divide by zero encountered in log log_transformed = np.array(c * np.log(1 + image), dtype = np.uint8)
<matplotlib.image.AxesImage at 0x7f5710fe9ac0>
# Linear Transformation
def pixelVal(pix, r1, s1, r2, s2):
if (0 <= pix and pix <= r1):
return (s1 / r1)*pix
elif (r1 < pix and pix <= r2):
return ((s2 - s1)/(r2 - r1)) * (pix - r1) + s1
else:
return ((255 - s2)/(255 - r2)) * (pix - r2) + s2
# Define parameters.
r1 = 7
s1 = 89
r2 = 190
s2 = 255
# Vectorize the function to apply it to each value in the Numpy array.
pixelVal_vec = np.vectorize(pixelVal)
# Apply contrast stretching.
contrast_stretch = pixelVal_vec(image, r1, s1, r2, s2)
# Save edited image.
cv2.imwrite('contrast_stretch.jpg', contrast_stretch)
imshow(contrast_stretch)
/usr/local/lib/python3.9/dist-packages/skimage/io/_plugins/matplotlib_plugin.py:150: UserWarning: Float image out of standard range; displaying image with stretched contrast. lo, hi, cmap = _get_display_range(image) WARNING:matplotlib.image:Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
<matplotlib.image.AxesImage at 0x7f56bc222a30>
# Image Denoising
denoised_image = cv2.fastNlMeansDenoisingColored(image, None, 12, 10, 8, 15)
# Save edited image.
cv2.imwrite('denoised_image.jpg', denoised_image)
imshow(denoised_image)
<matplotlib.image.AxesImage at 0x7f56bf5f2f70>
"""Histgograms help understand gray level occurence in an image"""
# Calculate Histogram for Colored Image
histr = cv2.calcHist([image],[0],None,[256],[0,256])
plt.plot(histr)
[<matplotlib.lines.Line2D at 0x7f56c613df10>]
# Calculate Histogram for gray image
grey_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
histogram = cv2.calcHist([grey_image], [0], None, [256], [0, 256])
plt.plot(histogram, color='k')
[<matplotlib.lines.Line2D at 0x7f56cd742a60>]